package com.nnnu.wsnackshop.listener;

import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.nnnu.wsnackshop.config.RabbitMQConfig;
import com.nnnu.wsnackshop.exception.ObjectException;
import com.nnnu.wsnackshop.mapper.ProductSkuMapper;
import com.nnnu.wsnackshop.pojo.dto.InventoryChangeDTO;
import com.nnnu.wsnackshop.pojo.entity.ProductSku;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

/**
 * 库存消息队列监听器
 * 只有当spring.rabbitmq.enabled为true时才启用
 */
@Component
@RequiredArgsConstructor
@Slf4j
@ConditionalOnProperty(name = "spring.rabbitmq.enabled", havingValue = "true", matchIfMissing = false)
public class InventoryMQListener {

    private final ProductSkuMapper productSkuMapper;

    /**
     * 监听库存减少队列
     */
    @RabbitListener(queues = RabbitMQConfig.INVENTORY_DECREASE_QUEUE)
    @Transactional(rollbackFor = Exception.class)
    public void handleInventoryDecrease(InventoryChangeDTO inventoryChangeDTO) {
        log.info("收到库存减少消息: {}", inventoryChangeDTO);

        // 库存减少消息中的quantity应该是负数，我们将其转为正数便于处理
        int decreaseAmount = Math.abs(inventoryChangeDTO.getQuantity());

        updateInventory(
                inventoryChangeDTO.getSkuId(),
                -decreaseAmount, // 负数表示减少库存
                inventoryChangeDTO.getProductName(),
                inventoryChangeDTO.getOrderNo(),
                "订单创建减库存");
    }

    /**
     * 监听库存增加队列
     */
    @RabbitListener(queues = RabbitMQConfig.INVENTORY_INCREASE_QUEUE)
    @Transactional(rollbackFor = Exception.class)
    public void handleInventoryIncrease(InventoryChangeDTO inventoryChangeDTO) {
        log.info("收到库存增加消息: {}", inventoryChangeDTO);

        // 库存增加消息中的quantity应该是正数
        int increaseAmount = inventoryChangeDTO.getQuantity();

        updateInventory(
                inventoryChangeDTO.getSkuId(),
                increaseAmount, // 正数表示增加库存
                inventoryChangeDTO.getProductName(),
                inventoryChangeDTO.getOrderNo(),
                "订单取消恢复库存");
    }

    /**
     * 更新库存的通用方法
     * 
     * @param skuId        SKU ID
     * @param changeAmount 变更数量（正数表示增加，负数表示减少）
     * @param productName  商品名称
     * @param orderNo      订单号
     * @param remark       备注
     */
    private void updateInventory(Integer skuId, int changeAmount, String productName, String orderNo, String remark) {
        // 使用乐观锁方式更新库存，防止并发问题
        boolean updateSuccess = false;
        int retryCount = 0;

        while (!updateSuccess && retryCount < 3) {
            // 查询当前库存
            ProductSku sku = productSkuMapper.selectById(skuId);
            if (sku == null) {
                log.warn("商品规格不存在，无法更新库存, skuId: {}", skuId);
                return;
            }

            log.info("当前库存信息: skuId={}, 当前库存={}", skuId, sku.getStock());

            // 如果是减库存操作，需要检查库存是否充足
            if (changeAmount < 0 && sku.getStock() < Math.abs(changeAmount)) {
                String errorMsg = String.format("商品[%s]库存不足: 当前库存=%d, 需要减少=%d",
                        productName, sku.getStock(), Math.abs(changeAmount));
                log.error(errorMsg);
                throw new ObjectException(errorMsg);
            }

            // 计算新库存
            int newStock = sku.getStock() + changeAmount;
            log.info("计算新库存: 当前库存={} {} {} = 新库存={}",
                    sku.getStock(),
                    (changeAmount >= 0 ? "+" : "-"),
                    Math.abs(changeAmount),
                    newStock);

            // 记录库存更改到特定日志文件，方便排查问题
            String logMessage = String.format(
                    "INVENTORY_UPDATE [%s] - SKU:%d, 商品:%s, 原库存:%d, 变更:%d, 新库存:%d, 订单号:%s, 备注:%s",
                    LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS")),
                    skuId,
                    productName,
                    sku.getStock(),
                    changeAmount,
                    newStock,
                    orderNo,
                    remark);

            System.out.println(logMessage);


            // 使用乐观锁更新库存
            LambdaUpdateWrapper<ProductSku> updateWrapper = new LambdaUpdateWrapper<>();
            updateWrapper.eq(ProductSku::getId, skuId)
                    .eq(ProductSku::getStock, sku.getStock()) // 乐观锁条件：当前库存等于查询时的库存
                    .set(ProductSku::getStock, newStock);

            int updateResult = productSkuMapper.update(null, updateWrapper);
            updateSuccess = updateResult > 0;
            log.info("库存更新结果: skuId={}, 更新行数={}, 是否成功={}",
                    skuId, updateResult, updateSuccess);

            retryCount++;

            if (!updateSuccess && retryCount < 3) {
                try {
                    log.info("库存更新失败，准备重试: skuId={}, 重试次数={}", skuId, retryCount);
                    // 短暂休眠后重试
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    log.error("库存更新被中断", e);
                    break;
                }
            }
        }

        if (!updateSuccess) {
            log.error("多次尝试更新库存失败: skuId={}, 变更数量={}", skuId, changeAmount);
            throw new ObjectException("更新商品库存失败，请稍后再试");
        }
    }
}